// SPDX-License-Identifier: Apache-2.0
import { eqDelta, roundTo } from "@thi.ng/math";
import { expect, test } from "bun:test";
import {
	bisectorStrategy,
	centroidStrategy,
	defuzz,
	firstOfMaximaStrategy,
	gaussian,
	invRamp,
	lastOfMaximaStrategy,
	meanOfMaximaStrategy,
	or,
	ramp,
	triangle,
	variable,
	type DefuzzStrategy,
} from "../src/index.js";

test("strategies", () => {
	// https://www.researchgate.net/publication/267041266_Introduction_to_fuzzy_logic
	const inputs = {
		food: variable([0, 10], {
			awful: invRamp(1, 3),
			delicious: ramp(7, 9),
		}),
		service: variable([0, 10], {
			poor: gaussian(0, 1.5),
			good: gaussian(5, 1.5),
			excellent: gaussian(10, 1.5),
		}),
	};

	const outputs = {
		tip: variable([0, 30], {
			low: triangle(0, 5, 10),
			medium: triangle(10, 15, 20),
			high: triangle(20, 25, 30),
		}),
	};

	type I = typeof inputs;
	type O = typeof outputs;

	// if service is poor OR food is awful -> tip is low
	// if service is normal -> tip is medium
	// if service is excellent OR food is delicious -> tip is high
	const rules = [
		or<I, O>({ food: "awful", service: "poor" }, { tip: "low" }),
		or<I, O>({ service: "good" }, { tip: "medium" }),
		or<I, O>({ food: "delicious", service: "excellent" }, { tip: "high" }),
	];

	const testStrategy = (
		id: string,
		strategy: DefuzzStrategy,
		expected: number[]
	) => {
		// const all = [];
		for (let i = 0, k = 0; i <= 10; i++) {
			for (let j = 0; j <= 10; j++, k++) {
				let res = defuzz(
					inputs,
					outputs,
					rules,
					{ food: i, service: j },
					strategy
				);
				expect(
					eqDelta(roundTo(res.tip!, 0.01), expected[k])
				).toBeTrue();
				// all.push(res.tip!.toFixed(2));
			}
		}
		// for (let i = 0; i <= 10; i++) {
		//     console.log(all.slice(i * 11, (i + 1) * 11).join(", "));
		// }
		// console.log("--");
	};

	// prettier-ignore
	const centroidResults = [
            5.08, 5.54, 7.02, 8.95, 9.91, 10.06, 10.32, 11.08, 13.18, 14.80, 15.00,
            5.08, 5.54, 7.02, 8.95, 9.91, 10.06, 10.32, 11.08, 13.18, 14.80, 15.00,
            5.08, 5.56, 7.52, 9.66, 10.62, 10.78, 11.07, 11.99, 14.41, 16.19, 16.42,
            5.08, 5.56, 7.79, 12.22, 14.46, 15.00, 15.54, 17.78, 22.21, 24.44, 24.92,
            5.08, 5.56, 7.79, 12.22, 14.46, 15.00, 15.54, 17.78, 22.21, 24.44, 24.92,
            5.08, 5.56, 7.79, 12.22, 14.46, 15.00, 15.54, 17.78, 22.21, 24.44, 24.92,
            5.08, 5.56, 7.79, 12.22, 14.46, 15.00, 15.54, 17.78, 22.21, 24.44, 24.92,
            5.08, 5.56, 7.79, 12.22, 14.46, 15.00, 15.54, 17.78, 22.21, 24.44, 24.92,
            13.58, 13.81, 15.59, 18.01, 18.93, 19.22, 19.38, 20.34, 22.48, 24.44, 24.92,
            15.00, 15.20, 16.82, 18.92, 19.68, 19.94, 20.09, 21.05, 22.98, 24.46, 24.92,
            15.00, 15.20, 16.82, 18.92, 19.68, 19.94, 20.09, 21.05, 22.98, 24.46, 24.92,
        ];
	testStrategy("centroid", centroidStrategy(), centroidResults);

	// prettier-ignore
	const bisectResults = [
            4.87, 5.00, 5.53, 6.91, 8.88, 10.30, 10.50, 8.32, 8.32, 11.41, 14.85,
            4.87, 5.00, 5.53, 6.91, 8.88, 10.30, 10.50, 8.32, 8.32, 11.41, 14.85,
            4.87, 5.03, 6.12, 8.30, 12.14, 12.39, 12.43, 11.82, 13.06, 21.81, 22.31,
            4.87, 5.03, 6.39, 13.32, 14.68, 14.85, 15.02, 16.38, 23.31, 24.67, 24.83,
            4.87, 5.03, 6.39, 13.32, 14.68, 14.85, 15.02, 16.38, 23.31, 24.67, 24.83,
            4.87, 5.03, 6.39, 13.32, 14.68, 14.85, 15.02, 16.38, 23.31, 24.67, 24.83,
            4.87, 5.03, 6.39, 13.32, 14.68, 14.85, 15.02, 16.38, 23.31, 24.67, 24.83,
            4.87, 5.03, 6.39, 13.32, 14.68, 14.85, 15.02, 16.38, 23.31, 24.67, 24.83,
            7.39, 7.89, 16.64, 17.88, 17.27, 17.31, 17.56, 21.40, 23.58, 24.67, 24.83,
            14.85, 18.29, 21.38, 21.38, 19.20, 19.40, 20.82, 22.79, 24.17, 24.70, 24.83,
            14.85, 18.29, 21.38, 21.38, 19.20, 19.40, 20.82, 22.79, 24.17, 24.70, 24.83,
        ];

	testStrategy("bisect", bisectorStrategy(), bisectResults);

	// prettier-ignore
	const foMaResults = [
            5.10, 5.10, 5.10, 5.10, 5.10, 15.00, 5.10, 5.10, 5.10, 5.10, 5.10,
            5.10, 5.10, 5.10, 5.10, 5.10, 15.00, 5.10, 5.10, 5.10, 5.10, 5.10,
            5.10, 4.20, 2.70, 2.70, 14.10, 15.00, 14.10, 2.70, 2.70, 24.30, 24.90,
            5.10, 4.20, 2.10, 12.30, 14.10, 15.00, 14.10, 12.30, 22.20, 24.30, 24.90,
            5.10, 4.20, 2.10, 12.30, 14.10, 15.00, 14.10, 12.30, 22.20, 24.30, 24.90,
            5.10, 4.20, 2.10, 12.30, 14.10, 15.00, 14.10, 12.30, 22.20, 24.30, 24.90,
            5.10, 4.20, 2.10, 12.30, 14.10, 15.00, 14.10, 12.30, 22.20, 24.30, 24.90,
            5.10, 4.20, 2.10, 12.30, 14.10, 15.00, 14.10, 12.30, 22.20, 24.30, 24.90,
            5.10, 4.20, 22.50, 22.50, 14.10, 15.00, 14.10, 22.50, 22.50, 24.30, 24.90,
            5.10, 24.90, 24.90, 24.90, 24.90, 15.00, 24.90, 24.90, 24.90, 24.90, 24.90,
            5.10, 24.90, 24.90, 24.90, 24.90, 15.00, 24.90, 24.90, 24.90, 24.90, 24.90,
        ];
	testStrategy("first", firstOfMaximaStrategy(), foMaResults);

	// prettier-ignore
	const loMaResults = [
            5.10, 5.10, 5.10, 5.10, 5.10, 15.00, 5.10, 5.10, 5.10, 5.10, 24.90,
            5.10, 5.10, 5.10, 5.10, 5.10, 15.00, 5.10, 5.10, 5.10, 5.10, 24.90,
            5.10, 5.70, 7.50, 7.50, 15.90, 15.00, 15.90, 7.50, 7.50, 25.80, 24.90,
            5.10, 5.70, 7.80, 17.70, 15.90, 15.00, 15.90, 17.70, 27.90, 25.80, 24.90,
            5.10, 5.70, 7.80, 17.70, 15.90, 15.00, 15.90, 17.70, 27.90, 25.80, 24.90,
            5.10, 5.70, 7.80, 17.70, 15.90, 15.00, 15.90, 17.70, 27.90, 25.80, 24.90,
            5.10, 5.70, 7.80, 17.70, 15.90, 15.00, 15.90, 17.70, 27.90, 25.80, 24.90,
            5.10, 5.70, 7.80, 17.70, 15.90, 15.00, 15.90, 17.70, 27.90, 25.80, 24.90,
            5.10, 5.70, 27.30, 27.30, 15.90, 15.00, 15.90, 27.30, 27.30, 25.80, 24.90,
            24.90, 24.90, 24.90, 24.90, 24.90, 15.00, 24.90, 24.90, 24.90, 24.90, 24.90,
            24.90, 24.90, 24.90, 24.90, 24.90, 15.00, 24.90, 24.90, 24.90, 24.90, 24.90,
        ];
	testStrategy("last", lastOfMaximaStrategy(), loMaResults);

	// prettier-ignore
	const meoMaResults = [
            5.10, 5.10, 5.10, 5.10, 5.10, 15.00, 5.10, 5.10, 5.10, 5.10, 15.00,
            5.10, 5.10, 5.10, 5.10, 5.10, 15.00, 5.10, 5.10, 5.10, 5.10, 15.00,
            5.10, 4.95, 5.10, 5.10, 15.00, 15.00, 15.00, 5.10, 5.10, 25.05, 24.90,
            5.10, 4.95, 4.95, 15.00, 15.00, 15.00, 15.00, 15.00, 25.05, 25.05, 24.90,
            5.10, 4.95, 4.95, 15.00, 15.00, 15.00, 15.00, 15.00, 25.05, 25.05, 24.90,
            5.10, 4.95, 4.95, 15.00, 15.00, 15.00, 15.00, 15.00, 25.05, 25.05, 24.90,
            5.10, 4.95, 4.95, 15.00, 15.00, 15.00, 15.00, 15.00, 25.05, 25.05, 24.90,
            5.10, 4.95, 4.95, 15.00, 15.00, 15.00, 15.00, 15.00, 25.05, 25.05, 24.90,
            5.10, 4.95, 24.90, 24.90, 15.00, 15.00, 15.00, 24.90, 24.90, 25.05, 24.90,
            15.00, 24.90, 24.90, 24.90, 24.90, 15.00, 24.90, 24.90, 24.90, 24.90, 24.90,
            15.00, 24.90, 24.90, 24.90, 24.90, 15.00, 24.90, 24.90, 24.90, 24.90, 24.90,
        ];
	testStrategy("mean", meanOfMaximaStrategy(), meoMaResults);

	// const strat = instrumentStrategy(centroidStrategy(), fuzzySetToAscii());
	// defuzz(
	//     inputs,
	//     outputs,
	//     rules,
	//     { food: 7.32, service: 7.83 },
	//     strat,
	//     tnormAczelAlsina(2)
	// );
	// console.log(strat.deref()[0]);
});
